<?php

/**
 * feedback actions.
 *
 * @package    coverd
 * @subpackage feedback
 * @author     Your name here
 * @version    SVN: $Id: actions.class.php 23810 2009-11-12 11:07:44Z Kris.Wallsmith $
 */
class feedbackActions extends sfActions
{
    protected function retreiveUserIdAndValidateSession()
    {
        $this->userId = $this->getUser()->getAttribute("id");
        
        $this->user = Doctrine_Core::getTable("User")->find($this->userId);
        $this->forward404Unless($this->user);
        
        $this->getResponse()->setSlot("loggedInUser", $this->user);
    }
    
    public function executeIndex(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        $this->userSources = Doctrine_Core::getTable('UserSource')
            ->createQuery('us')
            ->innerJoin('us.Source s')
            ->where('s.disabled = 0')
            ->andWhere('us.disabled = 0')
            ->andWhere('us.user_id = ?', $this->userId)
            ->execute();
        
        if ($this->userSources->count() == 0) {
            $this->redirect("feedback/addSource?no-sources=1");
            return;
        }
    }
    
    protected function parseFiltersAndCreateFeedbackQuery(sfWebRequest $request)
    {
        $query = Doctrine_Core::getTable('Feedback')
            ->createQuery('f')
            //->innerJoin('f.UserSource us ON f.source_id = us.source_id')
            ->where("(f.source_id IN (SELECT us.source_id FROM UserSource us INNER JOIN us.Source s ON us.source_id = s.id WHERE us.user_id = " . $this->userId . " AND us.disabled = 0 AND s.disabled = 0))")
            ->andWhere('submitted_date > "2000-01-01"'); // Lame. Sanity check to make sure we don't get any 1/1/1970 dates.
            
        $this->sourceIdMixed = $request->getParameter("sourceId", null);
        if (is_array($this->sourceIdMixed)) {
            $query->andWhere("f.source_id IN (" . implode(", ", $this->sourceIdMixed) . ")");
        }
        else if ($this->sourceIdMixed) {
            $query->andWhere("f.source_id = " . $this->sourceIdMixed);
        }
        
        $this->name = $request->getParameter("name", null);
        if ($this->name) {
            $query->andWhere("f.name LIKE '%" . $this->name . "%'");
        }
        
        $this->content = $request->getParameter("content", null);
        if ($this->content) {
            $query->andWhere("f.content LIKE '%" . $this->content . "%'");
        }
        
        $this->tagIdMixed = $request->getParameter("tagId", null);
        if ($this->tagIdMixed) {
            $tagIdArray = is_array($this->tagIdMixed) ? $this->tagIdMixed : array($this->tagIdMixed);
            $orSql = "";
            $sep = "";
            foreach ($tagIdArray as $tagId) {
                $alias = "ft" . $tagId;
                $orSql .= $sep . " f.id IN (SELECT " . $alias . ".feedback_id FROM FeedbackTag " . $alias . " WHERE " . $alias . ".tag_id = " . $tagId . ") ";
                $sep = " OR ";
            }
            $query->andWhere("(" . $orSql . ")");
            
            // TODO - implement as an "AND" query?
            //foreach ($tagIdArray as $tagId) {
            //    $query->andWhere("f.feedback_id IN (SELECT feedback_id FROM feedback_tag WHERE tag_id = ?)", $tagId);
            //}
        }
        
        $this->dateMode = $request->getParameter("date-mode", null);
        if ($this->dateMode && $this->dateMode != "all") {
            $this->fromDate = strtotime(date("Y-m-d"));
            $this->toDate = strtotime(date("Y-m-d"));
            
            // Determine the actual range
            if ($this->dateMode == "this-week") {
                $this->fromDate = strtotime(date("Y-m-d", strtotime("-" . date("w") . " day")));
            }
            else if ($this->dateMode == "last-week") {
                $this->fromDate = strtotime(date("Y-m-d", strtotime("-" . (7 + date("w")) . " day")));
                $this->toDate = strtotime(date("Y-m-d", strtotime("-" . date("w") . " day")));
            }
            else if ($this->dateMode == "this-month") {
                $this->fromDate = strtotime(date("Y-m-01"));
            }
            else if ($this->dateMode == "last-month") {
                $this->fromDate = strtotime(date("Y-m-01", strtotime("-1 month")));
                $this->toDate = strtotime(date("Y-m-t", strtotime("-1 month")));
            }
            else if ($this->dateMode == "this-year") {
                $this->fromDate = strtotime(date("Y-01-01"));
            }
            else if ($this->dateMode == "last-year") {
                $this->fromDate = strtotime(date("Y-01-01", strtotime("-1 year")));
                $this->toDate = strtotime(date("Y-12-31", strtotime("-1 year")));
            }
            else if ($this->dateMode == "custom") {
                $this->fromDate = strtotime($request->getParameter("date-mode-custom-from", null));
                $this->toDate = strtotime($request->getParameter("date-mode-custom-to", null));
            }
            
            // Add it to the query if we have correct values
            if ($this->fromDate > 0 && $this->toDate > 0) {
                $query->andWhere("f.submitted_date BETWEEN '" . date("Y-m-d", $this->fromDate) . "' AND '" . date("Y-m-d", $this->toDate) . "'");
            }
            else {
                $this->dateMode = "all";
            }
        }
        
        $this->ratingStars = $request->getParameter("rating-stars", null);
        $this->ratingComparison = $request->getParameter("rating-comparison", null);
        if ($this->ratingStars && $this->ratingComparison) {
            if (is_numeric($this->ratingStars) && $this->ratingStars >= 0 && $this->ratingStars <= 5) {
                $operator = "=";
                
                if ($this->ratingComparison == "greater-than") {
                    $operator = ">=";
                }
                else if ($this->ratingComparison == "less-than") {
                    $operator = "<=";
                }
                
                $query->andWhere("f.rating " . $operator . " " . $this->ratingStars);
            }
            else {
                $this->ratingStars = null;
                $this->ratingComparison = null;
            }
        }
        
        return $query;
    }
    
    public function executeList(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        // We'll need the sources list for the filter options
        
        $this->userSources = Doctrine_Core::getTable('UserSource')
            ->createQuery('us')
            ->innerJoin('us.Source s')
            ->where('s.disabled = 0')
            ->andWhere('us.disabled = 0')
            ->andWhere('us.user_id = ?', $this->userId)
            ->execute();
        
        // We'll need this for lookup
        $this->userSourceBySourceId = array();
        foreach ($this->userSources as $userSource) {
            $this->userSourceBySourceId[$userSource->getSourceId()] = $userSource;
        }
        
        // Get the feedback query
        
        $query = $this->parseFiltersAndCreateFeedbackQuery($request);
        
        //echo $query->getSqlQuery(); exit;
        
        // Find out how many total items we have
        
        $this->feedbackCount = $query->count();
        
        // Sort out the pagination
        
        $defaultPageSize = 20;
        $this->currentPage = null;
        $this->paginationLinks = array();
        
        $this->offset = $request->getParameter("offset", 0);
        $this->limit = $request->getParameter("limit", $defaultPageSize);
        
        // The base string
        $baseQueryString = "";
        if ($this->sourceIdMixed) {
            foreach ((is_array($this->sourceIdMixed) ? $this->sourceIdMixed : array($this->sourceIdMixed)) as $sourceId) {
                $baseQueryString .= "&sourceId=" . $sourceId;
            }
        }
        if ($this->name) {
            $baseQueryString .= "&name=" . $this->name;
        }
        if ($this->content) {
            $baseQueryString .= "&content=" . $this->content;
        }
        if ($this->tagIdMixed) {
            foreach ((is_array($this->tagIdMixed) ? $this->tagIdMixed : array($this->tagIdMixed)) as $tagId) {
                $baseQueryString .= "&tagId=" . $tagId;
            }
        }
        if ($this->dateMode) {
            $baseQueryString .= "&date-mode=" . $this->dateMode;
            if ($this->dateMode == "custom") {
                $baseQueryString .= "&date-mode-custom-from=" . date("Y-m-d", $this->dateFrom) . "&date-mode-custom-to=" . date("Y-m-d", $this->dateTo);
            }
        }
        if ($this->ratingStars && $this->ratingComparison) {
            $baseQueryString .= "&rating-stars=" . $this->ratingStars .  "&rating-comparison=" . $this->ratingComparison;
        }
        
        $this->currentPage = ($this->offset / $defaultPageSize) + 1;
        
        $currentOffset = 0;
        for ($i = 0; $currentOffset < $this->feedbackCount; $i++) {
            $this->paginationLinks[] = $this->getController()->genUrl("feedback/list") . "?" . $baseQueryString . "&offset=" . $currentOffset . "&limit=" . $defaultPageSize;
            
            $currentOffset += $defaultPageSize;
        }
        
        // Run the final query
            
        $query
            ->offset($this->offset)
            ->limit($this->limit)
            ->orderBy("f.submitted_date DESC, f.id DESC");
        
        $this->feedbackItems = $this->runQueryAndLogStats($query, "list");
        
        // We'll need the tags list for the filter options
        
        $this->tags = Doctrine_Core::getTable('Tag')
            ->createQuery('t')
            ->where('t.user_id = ?', $this->userId)
            ->orWhere('t.user_id IS NULL')
            ->orderBy('t.name')
            ->execute();
        
        $this->view = $request->getParameter("view", "list");
        
        // Only allow graphs for sources if there's more than 1 available to show
        $this->allowSourceChart = false;
        if (($this->sourceIdMixed && is_array($this->sourceIdMixed) && count($this->sourceIdMixed) > 1)
            || (!$this->sourceIdMixed && $this->userSources->count() > 1)
        ) {
            $this->allowSourceChart = true;
        }
    }
    
    protected function runQueryAndLogStats($query, $type) {
        
        LogHelper::log("Starting query for " . $type . ": " . $query->getSqlQuery(), "query");
        
        $startTime = time();
        $results = $query->execute();
        $runTime = time() - $startTime;
        
        LogHelper::log("Completed query, took " . $runTime . " seconds", "query");
        
        return $results;
    }
    
    public function executeChartData(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        $this->setLayout(false);
        
        $this->success = false;
        $this->data = array();
        $this->labels = array();
        $this->xSeriesTitle = "";
        $this->ySeriesTitle = "";
        
        $query = $this->parseFiltersAndCreateFeedbackQuery($request);
        
        //echo $query->getSqlQuery(); exit;
        
        $this->chartType = $request->getParameter("chart-type", null);
        $this->chartX = $request->getParameter("chart-x", null);
        $this->chartY = $request->getParameter("chart-y", null);
        $this->timePeriod = $request->getParameter("time-period", null);
        if ($this->chartType && ($this->chartX || ($this->chartY && $this->timePeriod))) {
            
            if ($this->chartType == "pie" || $this->chartType == "bar") {
                
                $this->ySeriesTitle = "Total count of feedback items";
                
                // Set up the grouping criteria
                
                $groupByField = "";
                if ($this->chartX == "sources") {
                    $groupByField = "f.source_id";
                    
                    $userSourceQuery = Doctrine_Core::getTable('UserSource')
                        ->createQuery('us')
                        ->innerJoin('us.Source s')
                        ->where('us.user_id = ?', $this->userId)
                        ->andWhere('us.disabled = 0')
                        ->andWhere('s.disabled = 0')
                        ->orderBy('s.id');
                    
                    if ($this->sourceIdMixed) {
                        $userSourceQuery->andWhere('s.id IN ?', array(is_array($this->sourceIdMixed) ? $this->sourceIdMixed : array($this->sourceIdMixed)));
                    }
                    
                    $userSources = $userSourceQuery->execute();
                    foreach ($userSources as $userSource) {
                        $this->labels[] = ($userSource->getName() ? $userSource->getName() : $userSource->Source->getName());
                    }
                    
                    $this->xSeriesTitle = "Items of feedback";
                }
                else if ($this->chartX == "ratings") {
                    $groupByField = "f.rating";
                    
                    $this->labels = array("1 star", "2 stars", "3 stars", "4 stars", "5 stars");
                    $this->xSeriesTitle = "Ratings";
                }
                else if ($this->chartX == "tags") {
                    
                    // This requires re-arranging the query (because of a weird Doctrine issue). Shouldn't affect anything.
                    $groupByField = "ft.tag_id";
                    $query->from("FeedbackTag ft");
                    $query->innerJoin("ft.Feedback f");
                    
                    // If an explicit tag list was specified, only include these tags. Otherwise others may be included in multi-tagging scenarios.
                    if ($this->tagIdMixed) {
                        $tagIdArray = is_array($this->tagIdMixed) ? $this->tagIdMixed : array($this->tagIdMixed);
                        $query->andWhere("ft.tag_id IN (" . implode(", ", $tagIdArray) . ")");
                    }
                    
                    // Labels will be done later when we know what kind of results we get
                    
                    $this->xSeriesTitle = "Tags";
                }
                
                // Run the query
                
                $query
                    ->select($groupByField . " AS the_group, COUNT(*) AS the_count")
                    ->groupBy($groupByField)
                    ->orderBy($groupByField);
                
                //echo $query->getSqlQuery();exit;
                
                $results = $this->runQueryAndLogStats($query, "chart-bar/pie");
                
                // Gather the results
                
                foreach ($results as $result) {
                    $this->data[$result["the_group"]] = $result["the_count"];
                }
                
                // Any post-processing to ensure order of results
                
                if ($this->chartX == "ratings") {
                    $output = array();
                    
                    foreach (array(1, 2, 3, 4, 5) as $label) {
                        $output[] = (isset($this->data[$label]) ? $this->data[$label] : 0);
                    }
                    
                    $this->data = $output;
                }
                else if ($this->chartX == "tags") {
                    $tagIds = array_keys($this->data);
                    
                    if (count($tagIds)) {
                        $tags = Doctrine_Core::getTable('Tag')
                            ->createQuery('t')
                            ->where('t.id IN ?', array($tagIds))
                            ->orderBy('t.id')
                            ->execute();
                        foreach ($tags as $tag) {
                            $this->labels[] = $tag->getName();
                        }
                    }
                }
                
                // The final data should just be a data array, not associative
                $this->data = array_values($this->data);
                
                $this->success = true;
            }
            else if ($this->chartType == "line") {
                    
                $this->xSeriesTitle = "All";
                
                // Prepare the conditions
                
                if ($this->timePeriod == "year") {
                    $groupByField = "DATE_FORMAT(submitted_date, '%Y-01-01')";
                }
                else if ($this->timePeriod == "month") {
                    $groupByField = "DATE_FORMAT(submitted_date, '%Y-%m-01')";
                }
                else {
                    $groupByField = "DATE(submitted_date)";
                }
                
                if ($this->chartY == "average-rating") {
                    $aggregateField = "AVG(rating)";
                    $this->ySeriesTitle = "Average rating of feedback items";
                }
                else {
                    $aggregateField = "COUNT(*)";
                    $this->ySeriesTitle = "Total count of feedback items";
                }
                
                // Run the query
                
                $query
                    ->select($groupByField . " AS the_group, " . $aggregateField . " AS the_aggregate")
                    ->andWhere('submitted_date > "2000-01-01"') // Lame. Sanity check to make sure we don't get any 1/1/1970 dates.
                    ->groupBy($groupByField)
                    ->orderBy($groupByField);
                
                //$results = $this->runQueryAndLogStats($query, "chart-line");
                
                // This is lame. We have to manually run this query, not using the Doctrine object. This is because Doctrine has
                // a bug where you can't use functions (e.g. DATE_FORMAT()) in the GROUP BY clause, it doesn't hydrate the results
                // properly. This is why all the logic building this query doesn't use "?" parameters, but actually puts the condition
                // values manually in the query. TODO Find a better solution for this.
                LogHelper::log("Starting query for chart-line: " . $query->getSqlQuery(), "query");
                $statement = Doctrine_Manager::getInstance()->connection();
                $startTime = time();
                $results = $statement->execute($query->getSqlQuery())->fetchAll();
                $runTime = time() - $startTime;
                LogHelper::log("Completed query, took " . $runTime . " seconds", "query");
            
                // Gather the results
                
                foreach ($results as $result) {
                    // Use the numerical array indexes (instead of "the_aggregate") because of the above described limitation for this query
                    $data[$result[0]] = $result[1];
                }
                
                // Determine the labels. Use the full dates of the first date of the period to allow the grouping.
                
                $groupsToMatch = array();
                $this->labels = array();
                
                $keysArray = array_keys($data);
                
                $firstDate = (($this->dateMode != "all" && $this->fromDate > 0) ? $this->fromDate : strtotime($keysArray[0]));
                $lastDate = (($this->dateMode != "all" && $this->toDate > 0) ? $this->toDate : strtotime($keysArray[count($keysArray) - 1]));
                
                if ($this->timePeriod == "year") {
                    $firstDate = strtotime(date("Y-01-01", $firstDate));
                    $lastDate = strtotime(date("Y-01-01", $lastDate));
                }
                else if ($this->timePeriod == "month") {
                    $firstDate = strtotime(date("Y-m-01", $firstDate));
                    $lastDate = strtotime(date("Y-m-01", $lastDate));
                }
                
                $currentDate = $firstDate;
                while ($currentDate <= $lastDate) {
                    
                    $groupsToMatch[] = date("Y-m-d", $currentDate);
                    
                    if ($this->timePeriod == "year") {
                        $this->labels[] = date("Y", $currentDate);
                        $currentDate = strtotime(date("Y-m-d", $currentDate) . " +1 year");
                    }
                    else if ($this->timePeriod == "month") {
                        $this->labels[] = date("M 'y", $currentDate);
                        $currentDate = strtotime(date("Y-m-d", $currentDate) . " +1 month");
                    }
                    else {
                        $this->labels[] = date("jS M", $currentDate);
                        $currentDate += (24 * 60 * 60);
                    }
                }
                
                // Format the data for these labels, to add deficient entries
                
                foreach ($groupsToMatch as $groupToMatch) {
                    $this->data[] = (isset($data[$groupToMatch]) ? $data[$groupToMatch] : 0);
                }
                
                $this->success = true;
            }
            else if ($this->chartType == "scatter") {
                
                // Run the query
                
                $query
                    ->orderBy("f.submitted_date");
                
                //echo $query->getSqlQuery();exit;
                
                $results = $this->runQueryAndLogStats($query, "chart-scatter");
                
                // Gather the results
                
                foreach ($results as $result) {
                    $this->data[] = array(date("Y-m-d", strtotime($result->getSubmittedDate())), $result->getRating());
                }
                
                $this->xSeriesTitle = "Ratings";
                $this->ySeriesTitle = "Rating";
                
                $this->success = true;
            }
        }
    }
    
    public function executeUpdate(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        // Get the list of user sources first
        
        $userSources = Doctrine_Core::getTable('UserSource')
            ->createQuery('us')
            ->innerJoin('us.Source s')
            ->where('s.disabled = 0')
            ->andWhere('us.disabled = 0')
            ->andWhere('us.user_id = ?', $this->userId)
            ->execute();
        
        $this->userSourcesById = array();
        foreach ($userSources as $userSource) {
            $this->userSourcesById[$userSource->Source->getId()] = $userSource;
        }
        
        // Now get the list of recent runs
        
        $this->runs = Doctrine_Core::getTable('SourceRefreshRun')
            ->createQuery('srr')
            ->innerJoin('srr.Source s')
            ->innerJoin('s.UserSource us')
            ->where('s.disabled = 0')
            ->andWhere('us.disabled = 0')
            ->andWhere('us.user_id = ?', $this->userId)
            ->orderBy('CASE
                        WHEN srr.end_date IS NULL AND srr.start_date IS NOT NULL THEN 1
                        WHEN srr.end_date IS NULL THEN 2
                        ELSE 3
                    END,
                    CASE
                        WHEN srr.end_date IS NULL AND srr.start_date IS NOT NULL THEN srr.start_date
                        WHEN srr.end_date IS NULL THEN srr.requested_date
                        ELSE srr.end_date
                    END DESC')
            ->limit(40) // TODO Pagination?
            ->execute();
        
        $this->hideDescriptionMessage = false;
        if ($request->getCookie("hideDescriptionMessage") === "true") {
            $this->hideDescriptionMessage = true;
        }
    }
    
    public function executeUpdateHistory(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        $this->setLayout(false);
        $this->setTemplate("json");
        $this->success = false;
        $this->output = array();
        
        $sourceId = $request->getParameter("sourceId", null);
        $this->forward404Unless($sourceId);
        
        // Get details of the user source
        
        $userSource = Doctrine_Core::getTable('UserSource')
            ->createQuery('us')
            ->innerJoin('us.Source s')
            ->where('s.disabled = 0')
            ->andWhere('us.disabled = 0')
            ->andWhere('us.user_id = ?', $this->userId)
            ->andWhere('us.source_id = ?', $sourceId)
            ->fetchOne();
        $this->forward404Unless($userSource);
        $this->output["userSource"] = array(
            "userSourceId" => $userSource->getId(),
            "sourceId" => $userSource->getSourceId(),
            "name" => $userSource->getDisplayName(),
            "iconHtml" => DisplayHelper::printIconWithRoundedCorners($userSource->Source->getFinalIconUrl()),
            "providerIconHtml" => DisplayHelper::createProviderIconHtml($userSource->Source->getProvider()),
        );
        
        // Now get the list of recent runs for that source
        
        $runs = Doctrine_Core::getTable('SourceRefreshRun')
            ->createQuery('srr')
            ->where('srr.source_id = ?', $sourceId)
            ->orderBy('CASE
                        WHEN srr.end_date IS NULL AND srr.start_date IS NOT NULL THEN 1
                        WHEN srr.end_date IS NULL THEN 2
                        ELSE 3
                    END,
                    CASE
                        WHEN srr.end_date IS NULL AND srr.start_date IS NOT NULL THEN srr.start_date
                        WHEN srr.end_date IS NULL THEN srr.requested_date
                        ELSE srr.end_date
                    END DESC')
            ->limit(10)
            ->execute();
        $this->output["runs"] = array();
        foreach ($runs as $run) {
            if (strtotime($run->getRequestedDate()) > 0 || strtotime($run->getStartDate()) > 0) {
                $timeTaken = (strtotime($run->getEndDate()) - strtotime($run->getStartDate()) > 0 ? strtotime($run->getEndDate()) - strtotime($run->getStartDate()) : 0);
                
                $this->output["runs"][] = array(
                    "feedbackCount" => $run->getFeedbackCount(),
                    "startDateFuzzy" => ($run->getStartDate() > 0 ? DateHelper::fuzzyDateDetailed($run->getStartDate()) : null),
                    "requestedDateFuzzy" => ($run->getRequestedDate() > 0 ? DateHelper::fuzzyDateDetailed($run->getRequestedDate()) : null),
                    "endDateFuzzy" => ($run->getEndDate() > 0 ? DateHelper::fuzzyDateDetailed($run->getEndDate()) : null),
                    "timeTaken" => $timeTaken,
                    "complete" => (strtotime($run->getEndDate()) > 0),
                    "running" => (strtotime($run->getEndDate()) == 0 && strtotime($run->getStartDate()) > 0),
                    "requested" => (strtotime($run->getRequestedDate()) > 0),
                );
            }
        }
    }
    
    public function executeUpdateStatus(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        $this->setLayout(false);
        $this->setTemplate("json");
        $this->success = false;
        $this->output = array();
        
        $this->output["pendingRunCount"] = Doctrine_Core::getTable('SourceRefreshRun')
            ->createQuery('srr')
            ->innerJoin('srr.Source s')
            ->innerJoin('s.UserSource us')
            ->where('s.disabled = 0')
            ->andWhere('us.disabled = 0')
            ->andWhere('us.user_id = ?', $this->userId)
            ->andWhere('srr.start_date IS NULL')
            ->andWhere('srr.end_date IS NULL')
            ->count();
        
        //$this->output["runningRunCount"] = Doctrine_Core::getTable('SourceRefreshRun')
        //    ->createQuery('srr')
        //    ->innerJoin('srr.Source s')
        //    ->innerJoin('s.UserSource us')
        //    ->where('s.disabled = 0')
        //    ->andWhere('us.disabled = 0')
        //    ->andWhere('us.user_id = ?', $this->userId)
        //    ->andWhere('srr.start_date IS NOT NULL')
        //    ->andWhere('srr.end_date IS NULL')
        //    ->count();
        
        $runningUpdate = Doctrine_Core::getTable('SourceRefreshRun')
            ->createQuery('srr')
            ->innerJoin('srr.Source s')
            ->innerJoin('s.UserSource us')
            ->where('s.disabled = 0')
            ->andWhere('us.disabled = 0')
            ->andWhere('us.user_id = ?', $this->userId)
            ->andWhere('srr.start_date IS NOT NULL')
            ->andWhere('srr.end_date IS NULL')
            ->limit(1)
            ->execute();
        if ($runningUpdate && $runningUpdate->count() >= 1) {
            // Need to find the name for this source
            $userSource = Doctrine_Core::getTable('UserSource')
                ->createQuery('us')
                ->andWhere('us.disabled = 0')
                ->andWhere('us.user_id = ?', $this->userId)
                ->andWhere('us.source_id = ?', $runningUpdate[0]->getSourceId())
                ->fetchOne();
            
            $this->output["currentRun"] = array(
                "sourceId" => $runningUpdate[0]->getSourceId(),
                "userSourceId" => $userSource->getId(),
                "name" => $userSource->getDisplayName(), 
            );
        }
        
        $this->success = true;
    }
    
    public function executeSaveTags(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        $this->setLayout(false);
        
        $this->success = false;
        
        $feedbackId = $request->getParameter("feedbackId", null);
        $this->forward404Unless($feedbackId);
        
        // It's fine if this is empty - the user might be trying to remove all tags
        $tags = $request->getParameter("tags", array());
        
        // Add any new tags - we'll return these to the client
        
        $this->newTags = array();
        
        $feedbackTagTagIdsToAdd = array();
        foreach ($tags as $tag) {
            if (!(is_numeric($tag) || is_integer($tag))) {
                
                // Let's just double-check that there isn't already a custom tag called this - the client side may
                // have got confused
                
                $matching = Doctrine_Core::getTable("Tag")
                    ->createQuery("t")
                    ->where("t.name = ?", $tag)
                    ->andWhere("t.user_id = ?", $this->userId)
                    ->limit(1)
                    ->execute();
                if ($matching && is_object($matching) && $matching->count() > 0) {
                    $feedbackTagTagIdsToAdd[] = $matching[0]->getId();
                }
                else {
                    // Ok, let's create the new custom tag for this user
                    
                    $newTag = new Tag;
                    $newTag->setName($tag);
                    $newTag->setUserId($this->userId);
                    $newTag->save();
                    
                    $this->newTags[$tag] = $newTag;
                    $feedbackTagTagIdsToAdd[] = $newTag->getId();
                }
            }
            else {
                $feedbackTagTagIdsToAdd[] = $tag;
            }
        }
        
        // Easiest way to do this is to delete all tag associations and add them all afresh
        
        // Delete all
        Doctrine_Core::getTable("FeedbackTag")
            ->createQuery("ft")
            ->where("ft.feedback_id = ?", $feedbackId)
            ->delete()
            ->execute();
        
        // Add all
        foreach ($feedbackTagTagIdsToAdd as $tagId) {
            $addedTag = new FeedbackTag;
            $addedTag->setFeedbackId($feedbackId);
            $addedTag->setTagId($tagId);
            $addedTag->save();
        }
        
        $this->feedbackTags = $feedbackTagTagIdsToAdd;
        $this->success = true;
    }
    
    public function executeSetup(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
    }
    
    public function executeAddSource(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        $this->addingFirstSource = ($request->getParameter("no-sources", 0) == 1);
    }
    
    public function executeSearchAppStore(sfWebRequest $request)
    {
        $this->setLayout(false);
        $this->setTemplate("searchProvider");
        $this->results = array();
        $this->success = false;
        
        $text = $request->getParameter("text", null);
        $this->forward404Unless($text);
        
        $countryCode = $request->getParameter("country", "au");
        $countryConfig = Config::$countries[$countryCode];
        $this->forward404Unless($countryConfig);
        $this->forward404Unless($countryConfig["itunes"]);
        
        $data = file_get_contents("http://itunes.apple.com/search?country=" . $countryConfig["itunes"] . "&entity=software&term=" . urlencode($text));
        //$data = json_encode(array(
        //    "resultCount" => 2,
        //    "results" => array(
        //        array(
        //            "trackId" => "123214321",
        //            "trackName" => "blah",
        //            "artistName" => "yo",
        //            "artworkUrl60" => "hmm.png",
        //        ),
        //        array(
        //            "trackId" => "12321422321",
        //            "trackName" => "uh huh",
        //            "artistName" => "booyah",
        //            "artworkUrl60" => "hmm.png",
        //        )
        //    )
        //));
        //$data = json_encode(array(
        //    "resultCount" => 0,
        //    "results" => array()
        //));
        if ($data) {
            $jsonData = json_decode($data, true);
            if ($jsonData && is_array($jsonData)) {
                $this->success = true;
                
                if ($jsonData["resultCount"] >= 1 && is_array($jsonData["results"])) {
                    foreach ($jsonData["results"] as $result) {
                        $this->results[] = array(
                            "id" => $result["trackId"],
                            "name" => $result["trackName"],
                            "publisher" => $result["artistName"],
                            "icon" => $result["artworkUrl60"],
                        );
                    }
                }
            }
        }
    }
    
    public function executeSearchGooglePlay(sfWebRequest $request)
    {
        $this->setLayout(false);
        $this->setTemplate("searchProvider");
        $this->results = array();
        $this->success = false;
        
        $text = $request->getParameter("text", null);
        $this->forward404Unless($text);
        
        $data = file_get_contents("https://42matters.com/api/1/apps/search.json?access_token=dfeb9847ecc944eac21c33d604c46aeb4885b1fc&q=" . urlencode($text));
        //$data = json_encode(array(
        //    "number_results" => 2,
        //    "items" => array(
        //        array(
        //            "package_name" => "com.yeah.blah",
        //            "name" => "blah",
        //            "developer" => "yo",
        //            "icon" => "hmm.png",
        //        )
        //    )
        //));
        if ($data) {
            $jsonData = json_decode($data, true);
            if ($jsonData && is_array($jsonData)) {
                $this->success = true;
                
                if (is_array($jsonData["results"]) && count($jsonData["results"]) > 0) {
                    foreach ($jsonData["results"] as $result) {
                        $this->results[] = array(
                            "id" => $result["package_name"],
                            "name" => $result["title"],
                            "publisher" => $result["developer"],
                            "icon" => $result["icon"],
                        );
                    }
                }
            }
        }
    }
    
    public function executeSearchTwitter(sfWebRequest $request)
    {
        $this->setLayout(false);
        $this->setTemplate("searchProvider");
        $this->results = array();
        $this->success = false;
        
        $text = $request->getParameter("text", null);
        $this->forward404Unless($text);
        
        $consumerKey = "QeVMKJv0chHAlgK0FL1dOA";
        $consumerSecret = "e8mUi4PinH6iOcCqjYBNOr7ZWSHOVwjrA6vbRARYtM";
        
        $encodedBearerToken = base64_encode(urlencode($consumerKey) . ":" . urlencode($consumerSecret));
        
        $authHandle = curl_init();
        curl_setopt($authHandle, CURLOPT_URL, "https://api.twitter.com/oauth2/token");
        curl_setopt($authHandle, CURLOPT_HTTPHEADER, array(
            'Authorization: Basic ' . $encodedBearerToken,
            'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
        ));
        curl_setopt($authHandle, CURLOPT_POSTFIELDS, "grant_type=client_credentials");
        curl_setopt($authHandle, CURLOPT_RETURNTRANSFER,true);
        $rawResult = curl_exec($authHandle);
        curl_close($authHandle);
        
        $result = json_decode($rawResult, true);
        
        if ($result && is_array($result) && $result["token_type"] == "bearer") {
            
            $searchHandle = curl_init();
            curl_setopt($searchHandle, CURLOPT_URL, "https://api.twitter.com/1.1/search/tweets.json?count=20&include_entities=true&result_type=mixed&lang=en&q=" . urlencode($text));
            curl_setopt($searchHandle, CURLOPT_HTTPHEADER, array(
                'Authorization: Bearer ' . $result["access_token"],
            ));
            curl_setopt($searchHandle, CURLOPT_RETURNTRANSFER,true);
            $rawResult = curl_exec($searchHandle);
            curl_close($searchHandle);
            
            $result = json_decode($rawResult, true);
            
            if ($result && is_array($result)) {
                $this->success = true;
                
                if (is_array($result["statuses"]) && count($result["statuses"]) > 0) {
                    foreach ($result["statuses"] as $status) {
                        $this->results[] = array(
                            "tweet" => $status["text"],
                            "user" => $status["user"]["screen_name"],
                            "avatar" => $status["user"]["profile_image_url"],
                        );
                    }
                }
            }
        }
    }
    
    public function executeDoAddSource(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        LogHelper::log("executeAddSource: in");
        
        $this->setLayout(false);
        $this->setTemplate("genericJson");
        $this->success = false;
        
        if ($this->user->isAllowedToAddMoreSources()) {
        
            LogHelper::log("executeAddSource: starting checks");
        
            $provider = $request->getParameter("provider", null);
            $this->forward404Unless($provider);
            
            // Make sure we support this provider
            $this->forward404Unless(in_array($provider, array_keys(Config::$providers)));
            
            // Retrieve the config for this provider
            $providerConfig = Config::$providers[$provider];
            $this->forward404Unless($providerConfig);
            
            // Use this to check that we have all the fields we need to save for this provider, and then to create an
            // array to save them all - we'll JSONify this later.
            $providerConfigToSave = array();
            foreach ($providerConfig["configFields"] as $requiredConfigField) {
                $value = $request->getParameter($requiredConfigField, null);
                $this->forward404Unless($value);
                $providerConfigToSave[$requiredConfigField] = $value;
            }
            
            $sourceId = null;
            
            // First check to see if this source already exists (only for apps)
            // TODO Get a better way to do this check!
            
            LogHelper::log("executeAddSource: starting existing check");
            
            $existingSources = Doctrine_Core::getTable("Source")
                ->createQuery("s")
                ->where("s.provider = ?", $provider)
                ->execute();
            foreach ($existingSources as $existingSource) {
                if ($existingSource->getProvider() == $provider
                    && $existingSource->getProviderConfigElement($providerConfig["uniqueIdField"]) == $providerConfigToSave[$providerConfig["uniqueIdField"]]
                ) {
                    if ($provider == "itunes") {
                        // iTunes has to do the country check too
                        if ($existingSource->getProviderConfigElement("country") == $providerConfigToSave["country"]) {
                            $sourceId = $existingSource->getId();
                            break;
                        }
                    }
                    else {
                        // For other sources, only the "uniqueIdField" matters
                        $sourceId = $existingSource->getId();
                        break;
                    }
                }
            }
            
            LogHelper::log("executeAddSource: completed existing check");
            
            $isNewSource = false;
            
            if (!$sourceId) {
                $isNewSource = true;
                
                // The source doesn't already exist - add it
            
                LogHelper::log("executeAddSource: creating source");
                
                $source = new Source;
                $source->setProvider($provider);
                $source->setProviderConfig(json_encode($providerConfigToSave));
                
                if ($provider == "twitter") {
                    $source->setName("Search: " . $providerConfigToSave["term"]);
                }
                
                $source->save();
                
                $sourceId = $source->getId();
                
                LogHelper::log("executeAddSource: updateSourceRecord");
                
                SourceUpdates::updateSourceRecord($source);
                
                $this->getUser()->setFlash("addInfo", "The source has been successfully added! Before you can start managing your feedback from this source, the source needs
                                           to be updated. An update has been requested and will be performed at the next available opportunity. See the 'Updates' page for more
                                           information on the progress of your update. When that is complete, go to the 'View feedback' page to start managing your feedback!");
            }
            else {
                $this->getUser()->setFlash("addInfo", "The source has been successfully added! You can go to your 'View feedback' page to start managing feedback from this source.");
            }
            
            LogHelper::log("executeAddSource: linking user source");
            
            // Link up this user to the source
            
            $userSource = new UserSource;
            $userSource->setUserId($this->userId);
            $userSource->setSourceId($sourceId);
            $userSource->save();
            
            LogHelper::log("executeAddSource: requesting refresh run");
            
            // Request a new update
            $run = new SourceRefreshRun;
            $run->setUserInitiatedId($this->userId);
            $run->setSourceId($sourceId);
            $run->setRequestedDate(date("Y-m-d H:i:s"));
            $run->setRequestedPriority($isNewSource ? 10 : 0);
            $run->save();
            
            LogHelper::log("executeAddSource: done");
            
            $this->success = true;
        }
    }
    
    public function executeDoEditNickname(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        $this->setLayout(false);
        $this->setTemplate("genericJson");
        $this->success = false;
        
        $id = $request->getParameter("id", null);
        $this->forward404Unless($id);
        
        $this->userSource = Doctrine_Core::getTable('UserSource')->find($id);
        $this->forward404Unless($this->userSource);
        
        $name = $request->getParameter("name", null);
        $this->forward404Unless($name);
        
        $this->userSource->setName($name);
        $this->userSource->save();
        
        $this->success = true;
    }
    
    public function executeDoDeleteSource(sfWebRequest $request)
    {   
        $this->retreiveUserIdAndValidateSession();
        
        $this->setLayout(false);
        $this->setTemplate("genericJson");
        $this->success = false;
        
        $id = $request->getParameter("id", null);
        $this->forward404Unless($id);
        
        $this->userSource = Doctrine_Core::getTable('UserSource')->find($id);
        $this->forward404Unless($this->userSource);
        $this->forward404Unless($this->userSource->getUserId() == $this->userId);
        
        $this->userSource->setDisabled(1);
        $this->userSource->save();
        
        $this->success = true;
    }
    
    public function executeDoRequestUpdate(sfWebRequest $request)
    {   
        $this->retreiveUserIdAndValidateSession();
        
        $sourceId = $request->getParameter("sourceId", null);
        $this->forward404Unless($sourceId);
        
        if ($sourceId == "all") {
            $userSources = Doctrine_Core::getTable('UserSource')
                ->createQuery('us')
                ->innerJoin('us.Source s')
                ->where('us.user_id = ?', $this->userId)
                ->andWhere('us.disabled = 0')
                ->andWhere('s.allow_updates = 1')
                ->execute();
            foreach ($userSources as $userSource) {
                $run = new SourceRefreshRun;
                $run->setUserInitiatedId($this->userId);
                $run->setSourceId($userSource->getSourceId());
                $run->setRequestedDate(date("Y-m-d H:i:s"));
                $run->save();
            }
            
            $this->getUser()->setFlash("updateInfo", "Update requested for all sources.");
        }
        else {
            $this->source = Doctrine_Core::getTable('Source')->find($sourceId);
            $this->forward404Unless($this->source);
            $this->forward404Unless($this->source->getAllowUpdates() == 1);
        
            $run = new SourceRefreshRun;
            $run->setUserInitiatedId($this->userId);
            $run->setSourceId($sourceId);
            $run->setRequestedDate(date("Y-m-d H:i:s"));
            $run->save();
            
            $this->getUser()->setFlash("updateInfo", "Update requested for " . $this->source->getName() . ".");
        }
        
        $this->redirect("feedback/index");
    }
    
    public function executeExportCsv(sfWebRequest $request)
    {
        $this->retreiveUserIdAndValidateSession();
        
        // Get the feedback query
        
        $query = $this->parseFiltersAndCreateFeedbackQuery($request);
        
        // Run the final query
            
        $query
            ->limit(1000)
            ->orderBy("f.submitted_date DESC, f.id DESC");
        
        $this->feedbackItems = $this->runQueryAndLogStats($query, "list");
        
        $this->headers = array("Source", "Date", "Title", "Content", "Name", "Rating", "Version", "Device");
        
        $this->rows = array();
        foreach ($this->feedbackItems as $feedbackItem) {
            $this->rows[] = array(
                $feedbackItem->Source->getName(),
                date("Y-m-d", strtotime($feedbackItem->getSubmittedDate())),
                $feedbackItem->getTitle(),
                $feedbackItem->getContent(),
                $feedbackItem->getName(),
                $feedbackItem->getRating(),
                $feedbackItem->getVersion(),
                $feedbackItem->getDeviceType()
            );
        }
        
        $this->setLayout(false);
        $this->setTemplate("genericCsv");
    }
}
